package com.poizon.infrastructure.idem.core.util.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.poizon.infrastructure.idem.core.util.IIdemUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.validation.constraints.NotBlank;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

import static com.poizon.infrastructure.idem.core.domain.value.config.IdemConstants.*;
import static com.poizon.infrastructure.idem.core.util.impl.AbstractIdemUtil.Check.preParamCheck;


/**
 * 工具类/幂等/抽象模版
 */
@Slf4j
@AllArgsConstructor
public abstract class AbstractIdemUtil implements IIdemUtil {
    protected ThreadPoolExecutor idemAsyncSubmitRespExecutor;

    protected static class Get {
        /**
         * 拼接幂等键前缀
         */
        public static String getIdemKey(@NotBlank String operationKey) {
            Assert.notBlank(operationKey, "[通用幂等框架] operationKey不能为空");
            String idemKey = StrUtil.format("{}:{}", IDEM_KEY_PREFIX, operationKey);
            return idemKey;
        }

        public static String getThreadName() {
            return StrUtil.format("{} - {}", Thread.currentThread().getId(), Thread.currentThread().getName());
        }
    }

    protected static class Check {
        /**
         * 预检查/幂等参数
         */
        protected static void preParamCheck(String operationKey, Supplier serviceRunnable, Long expireTime, TimeUnit timeUnit, String reqType, AtomicInteger retryCount) {
            Assert.notBlank(operationKey, "[通用幂等框架] operationKey不能为空");
            Assert.notNull(expireTime, "[通用幂等框架] expireTime不能为空");
            Assert.isTrue(expireTime > 0, "[通用幂等框架] expireTime必须大于0");
            Assert.notNull(serviceRunnable, "serviceRunnable不能为空");
            Assert.notNull(timeUnit, "[通用幂等框架] timeUnit不能为空");
            Assert.notBlank(reqType, "[通用幂等框架] reqType不能为空");
            Assert.notNull(retryCount, "[通用幂等框架] retryCount不能为空");
            Assert.isTrue(retryCount.get() > 0, "[通用幂等框架] retryCount不能小于0");
        }

        /**
         * 预检查/防重提交参数
         */
        protected static void preParamCheck(String operationKey, Long expireTime, Runnable serviceRunnable, Runnable repeatedRunnable, TimeUnit timeUnit) {
            Assert.notBlank(operationKey, "[通用幂等框架] operationKey不能为空");
            Assert.notNull(expireTime, "[通用幂等框架] expireTime不能为空");
            Assert.isTrue(expireTime > 0, "[通用幂等框架] expireTime必须大于0");
            Assert.notNull(serviceRunnable, "[通用幂等框架] serviceRunnable不能为空");
            Assert.notNull(timeUnit, "[通用幂等框架] timeUnit不能为空");
        }
    }


    /**
     * 模版方法/执行幂等
     *
     * @param operationKey    自定义幂等key
     * @param serviceRunnable 业务操作
     * @param expireTime      幂等key过期时间
     * @param timeUnit        时间单位
     * @param reqType         请求类型
     * @param retryCount      重试次数
     * @return
     */
    protected abstract Object executeIdemImpl(String operationKey, Supplier serviceRunnable, Long expireTime, TimeUnit timeUnit, String reqType, AtomicInteger retryCount);


    @Override
    public Object executeIdem(String operationKey, Supplier serviceRunnable, Long expireTime, TimeUnit timeUnit, String reqType, AtomicInteger retryCount) {
        preParamCheck(operationKey, serviceRunnable, expireTime, timeUnit, reqType, retryCount);
        return executeIdemImpl(operationKey, serviceRunnable, expireTime, timeUnit, reqType, retryCount);
    }

    @Override
    public Object executeIdem(String operationKey, Supplier serviceRunnable, Long expireTime, TimeUnit timeUnit, String reqType) {
        return executeIdem(operationKey, serviceRunnable, expireTime, timeUnit, reqType, new AtomicInteger(DEFAULT_MAX_RETRY_TIMES));
    }

    @Override
    public Object executeIdem(String operationKey, Supplier serviceRunnable, Long expireTime, TimeUnit timeUnit) {
        return executeIdem(operationKey, serviceRunnable, expireTime, timeUnit, DEFAULT_IDEM_REQ_TYPE);
    }

    @Override
    public Object executeIdem(String operationKey, Supplier serviceRunnable, Long expireTime) {
        return executeIdem(operationKey, serviceRunnable, expireTime, DEFAULT_IDEM_KEY_TIME_UNIT);
    }

    @Override
    public Object executeIdem(String operationKey, Supplier serviceRunnable) {
        return executeIdem(operationKey, serviceRunnable, DEFAULT_IDEM_KEY_EXPIRE_TIME);
    }

}

